home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Mail / pine3.92 / pico / os_unix.c < prev    next >
C/C++ Source or Header  |  1996-03-17  |  46KB  |  2,174 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: os_unix.c,v 4.101 1996/03/17 23:00:08 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Operating system dependent routines - Ultrix 4.1
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1996 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  *
  30.  * Notes:
  31.  *
  32.  * - SGI IRIX 4.0.1 port by:
  33.  *       johnb@edge.cis.mcmaster.ca,  2 April 1992
  34.  *
  35.  * - Dynix/PTX port by:
  36.  *       Donn Cave, UCS/UW, 15 April 1992
  37.  *
  38.  * - 3B2, 3b1/7300, SCO ports by:
  39.  *       rll@felton.felton.ca.us, 7 Feb. 1993
  40.  *
  41.  * - Altos System V (asv) port by:
  42.  *     Tim Rice <tim@trr.metro.net>    6 Mar 96
  43.  *
  44.  * - Probably have to break this up into separate os_type.c files since
  45.  *   the #ifdef's are getting a bit cumbersome.
  46.  *
  47.  */
  48.  
  49. #include     <stdio.h>
  50. #include    <errno.h>
  51. #include    <setjmp.h>
  52. #include    <pwd.h>
  53. #if    defined(sv4) || defined(ptx)
  54. #include    <stropts.h>
  55. #include    <poll.h>
  56. #endif
  57. #if    defined(POSIX)
  58. #include    <termios.h>
  59. #if    defined(a32) || defined(cvx)
  60. #include    <sys/ioctl.h>
  61. #endif
  62. #else
  63. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(ct) || defined(asv)
  64. #include    <termio.h>
  65. #if    defined(isc)
  66. #include    <sys/sioctl.h>
  67. #include        <sys/bsdtypes.h>
  68. #endif
  69. #else
  70. #include    <sgtty.h>
  71. #endif
  72. #endif    /* POSIX */
  73.  
  74. #include    "osdep.h"
  75. #include        "pico.h"
  76. #include    "estruct.h"
  77. #include        "edef.h"
  78. #include        "efunc.h"
  79. #include    <fcntl.h>
  80. #include    <sys/wait.h>
  81. #include    <sys/file.h>
  82. #include    <sys/types.h>
  83. #include    <sys/time.h>
  84. #if    defined(a32)
  85. #include    <sys/select.h>
  86. #endif
  87.  
  88. int timeout = 0;
  89.  
  90. /*
  91.  * Immediately below are includes and declarations for the 3 basic
  92.  * terminal drivers supported; POSIX, SysVR3, and BSD
  93.  */
  94. #ifdef    POSIX
  95.  
  96. struct termios nstate,
  97.         ostate;
  98. #else
  99. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(asv)
  100.  
  101. struct termio nstate,
  102.               ostate;
  103.  
  104. #else
  105. struct  sgttyb  ostate;                /* saved tty state */
  106. struct  sgttyb  nstate;                /* values for editor mode */
  107. struct  ltchars    oltchars;            /* old term special chars */
  108. struct  ltchars    nltchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  109. struct  tchars    otchars;            /* old term special chars */
  110. struct  tchars    ntchars = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  111.  
  112. #endif    /* sv3 || sgi || isc || asv */
  113. #endif    /* POSIX */
  114.  
  115. #if    defined(sv3) || defined(ct)
  116. /*
  117.  * Windowing structure to support JWINSIZE/TIOCSWINSZ/TIOCGWINSZ 
  118.  */
  119. #define ENAMETOOLONG    78
  120.  
  121. struct winsize {
  122.     unsigned short ws_row;       /* rows, in characters*/
  123.     unsigned short ws_col;       /* columns, in character */
  124.     unsigned short ws_xpixel;    /* horizontal size, pixels */
  125.     unsigned short ws_ypixel;    /* vertical size, pixels */
  126. };
  127. #endif
  128.  
  129. #if    defined(bsd) || defined(lnx)
  130. int    errno;                    /* ya, I know... */
  131. #endif
  132.  
  133. #if    defined(bsd) || defined(dyn) || defined(ct)
  134. #define    SIGTYPE int
  135. #else
  136. #define    SIGTYPE    void
  137. #endif
  138.  
  139. #ifdef    SIGCHLD
  140. static jmp_buf pico_child_state;
  141. static short   pico_child_jmp_ok, pico_child_done;
  142. #endif
  143.  
  144. #ifdef    MOUSE
  145. static int mexist = 0;        /* is the mouse driver installed? */
  146. static unsigned mnoop;
  147. #endif
  148.  
  149. #ifdef    ANSI
  150.     int      kbseq(KBESC_T *, int (*)(), int *);
  151.     SIGTYPE  do_hup_signal();
  152.     SIGTYPE  rtfrmshell();
  153. #ifdef    TIOCGWINSZ
  154.     SIGTYPE  winch_handler();
  155. #endif
  156. #ifdef    SIGCHLD
  157.     SIGTYPE  child_handler();
  158. #endif
  159.  
  160. #else
  161.     int      kbseq();
  162.     SIGTYPE  do_hup_signal();
  163.     SIGTYPE  rtfrmshell();
  164. #ifdef    TIOCGWINSZ
  165.     SIGTYPE  winch_handler();
  166. #endif
  167. #ifdef    SIGCHLD
  168.     SIGTYPE  child_handler();
  169. #endif
  170.  
  171. #endif    /* ANSI */
  172.  
  173.  
  174. /*
  175.  * for alt_editor arg[] building
  176.  */
  177. #define    MAXARGS    10
  178.  
  179. /*
  180.  * ttopen - this function is called once to set up the terminal device 
  181.  *          streams.  if called as pine composer, don't mess with
  182.  *          tty modes, but set signal handlers.
  183.  */
  184. ttopen()
  185. {
  186.     if(Pmaster == NULL){
  187. #ifdef    POSIX
  188.     tcgetattr (0, &ostate);
  189.     tcgetattr (0, &nstate);
  190.     nstate.c_lflag &= ~(ISIG | ICANON | ECHO | IEXTEN);
  191.     nstate.c_iflag &= ~ICRNL;
  192.     nstate.c_oflag &= ~(ONLCR | OPOST);
  193.     nstate.c_cc[VMIN] = 1;
  194.     nstate.c_cc[VTIME] = 0;
  195.     tcsetattr (0, TCSADRAIN, &nstate);
  196. #else
  197. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(ct) || defined(asv)
  198.     (void) ioctl(0, TCGETA, &ostate);
  199.     (void) ioctl(0, TCGETA, &nstate);    /** again! **/
  200.  
  201.     nstate.c_lflag &= ~(ICANON | ISIG | ECHO);    /* noecho raw mode  */
  202.     nstate.c_oflag &= ~(OPOST | ONLCR);
  203.     nstate.c_iflag &= ~ICRNL;
  204.         
  205.     nstate.c_cc[VMIN] = '\01';  /* minimum # of chars to queue  */
  206.     nstate.c_cc[VTIME] = '\0'; /* minimum time to wait for input */
  207.     (void) ioctl(0, TCSETA, &nstate);
  208. #else
  209.     ioctl(0, TIOCGETP, &ostate);        /* save old state */
  210.     ioctl(0, TIOCGLTC, &oltchars);        /* Save old lcharacters */
  211.     ioctl(0, TIOCGETC, &otchars);        /* Save old characters */
  212.     ioctl(0, TIOCGETP, &nstate);        /* get base of new state */
  213.     nstate.sg_flags |= RAW;
  214.     nstate.sg_flags &= ~(ECHO|CRMOD);    /* no echo for now... */
  215.     ioctl(0, TIOCSETP, &nstate);        /* set mode */
  216.  
  217.     ioctl(0, TIOCSLTC, &nltchars);        /* put new lcharacter into K */
  218.     ioctl(0, TIOCSETC, &ntchars);        /* put new character into K */
  219. #endif    /* sv3 */
  220. #endif    /* POSIX */
  221. #ifdef    MOUSE
  222.     if(gmode & MDMOUSE)
  223.       init_mouse();
  224. #endif    /* MOUSE */
  225.     }
  226.  
  227. #ifdef    MOUSE
  228.     if(mexist)
  229.       kpinsert(&pico_kbesc, "\033[M", K_XTERM_MOUSE);
  230. #endif    /* MOUSE */
  231.  
  232.     if(!Pmaster || (gmode ^ MDEXTFB))
  233.       picosigs();
  234.  
  235.     return(1);
  236. }
  237.  
  238.  
  239. /*
  240.  * ttresize - recompute the screen dimensions if necessary, and then
  241.  *          adjust pico's internal buffers accordingly.
  242.  */
  243. int
  244. ttresize ()
  245. {
  246.     int row = -1, col = -1;
  247.  
  248.     ttgetwinsz(&row, &col);
  249.     resize_pico(row, col);
  250. }
  251.  
  252.  
  253. /*
  254.  * picosigs - Install any handlers for the signals we're interested
  255.  *          in catching.
  256.  */
  257. picosigs()
  258. {
  259.     signal(SIGHUP,  do_hup_signal);    /* deal with SIGHUP */
  260.     signal(SIGTERM, do_hup_signal);    /* deal with SIGTERM */
  261. #ifdef    SIGTSTP
  262.     signal(SIGTSTP, SIG_DFL);
  263. #endif
  264. #ifdef    TIOCGWINSZ
  265.     signal(SIGWINCH, winch_handler); /* window size changes */
  266. #endif
  267. }
  268.  
  269.  
  270. /*
  271.  * ttclose - this function gets called just before we go back home to 
  272.  *           the command interpreter.  If called as pine composer, don't
  273.  *           worry about modes, but set signals to default, pine will 
  274.  *           rewire things as needed.
  275.  */
  276. ttclose()
  277. {
  278.     if(Pmaster){
  279.     signal(SIGHUP, SIG_DFL);
  280. #ifdef    SIGCONT
  281.     signal(SIGCONT, SIG_DFL);
  282. #endif
  283. #ifdef    TIOCGWINSZ
  284.     signal(SIGWINCH, SIG_DFL);
  285. #endif
  286.     }
  287.     else{
  288. #ifdef    POSIX
  289.     tcsetattr (0, TCSADRAIN, &ostate);
  290. #else
  291. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(ct) || defined(asv)
  292.         ioctl(0, TCSETA, &ostate);
  293. #else
  294.     ioctl(0, TIOCSETP, &ostate);
  295.     ioctl(0, TIOCSLTC, &oltchars);
  296.     ioctl(0, TIOCSETC, &otchars);
  297.  
  298.     /*
  299.      * This works around a really weird problem.  On slow speed lines,
  300.      * if an exit happens with some number of characters still to be
  301.      * written in the terminal driver, one or more characters will 
  302.      * be changed when they finally get drained.  This can be reproduced
  303.      * on a 2400bps line, writing a multi-line buffer on exit using
  304.      * a vt100 type terminal.  It turns out the last char in the
  305.      * escape sequence turning off reverse video was getting changed
  306.      * from 'm' to ' '.  I said it was weird.
  307.      */
  308.     if(ostate.sg_ospeed <= B2400)
  309.       sleep(1);
  310. #endif
  311. #endif    /* POSIX */
  312. #ifdef    MOUSE
  313.     end_mouse();
  314. #endif
  315.     }
  316.  
  317.     return(1);
  318. }
  319.  
  320.  
  321. /*
  322.  * ttspeed - return TRUE if tty line speed < 9600 else return FALSE
  323.  */
  324. ttisslow()
  325. {
  326. #if    defined(POSIX)
  327.     struct termios tty;
  328.  
  329.     return((tcgetattr (1, &tty) == 0) ? cfgetospeed (&tty) < B9600 : FALSE);
  330. #else
  331. #if    defined(sv3) || defined(sgi) || defined(isc) || defined(asv)
  332.     struct termio tty;
  333.  
  334. #ifdef asv
  335.     return((ioctl(1, TCGETA, &tty) == 0) ? (tty.c_cflag && CBAUD) < B9600 : FALSE);
  336. #else /* asv */
  337.     return((tcgetattr (1, &tty) == 0) ? cfgetospeed (&tty) < B9600 : FALSE);
  338. #endif /* asv */
  339. #else
  340.     struct  sgttyb tty;
  341.  
  342.     return((ioctl(1, TIOCGETP, &tty) == 0) ? tty.sg_ospeed < B9600 : FALSE);
  343. #endif
  344. #endif
  345. }
  346.  
  347.  
  348. /*
  349.  * ttgetwinsz - set global row and column values (if we can get them)
  350.  *        and return.
  351.  */
  352. ttgetwinsz(row, col)
  353.     int *row, *col;
  354. {
  355.     if(*row < 0)
  356.       *row = NROW - 1;
  357.     if(*col <= 0)
  358.       *col = NCOL;
  359. #ifdef TIOCGWINSZ
  360.     {
  361.     struct winsize win;
  362.  
  363.     if (ioctl(0, TIOCGWINSZ, &win) == 0) {    /* set to anything useful.. */
  364.         if(win.ws_row)            /* ... the tty drivers says */
  365.           *row = win.ws_row - 1;
  366.  
  367.         if(win.ws_col)
  368.           *col = win.ws_col;
  369.     }
  370.  
  371.     signal(SIGWINCH, winch_handler);    /* window size changes */
  372.     }
  373. #endif
  374. }
  375.  
  376.  
  377. /*
  378.  * ttputc - Write a character to the display. 
  379.  */
  380. ttputc(c)
  381. {
  382.     return(putc(c, stdout));
  383. }
  384.  
  385.  
  386. /*
  387.  * ttflush - flush terminal buffer. Does real work where the terminal 
  388.  *           output is buffered up. A no-operation on systems where byte 
  389.  *           at a time terminal I/O is done.
  390.  */
  391. ttflush()
  392. {
  393.     return(fflush(stdout));
  394. }
  395.  
  396.  
  397. /*
  398.  * ttgetc - Read a character from the terminal, performing no editing 
  399.  *          and doing no echo at all.
  400.  */
  401. ttgetc()
  402. {
  403.     unsigned char c;
  404.     int i;
  405.  
  406.     if((i = read(0, &c, 1)) == 1)            /* success */
  407.       return((int) c);
  408.     else if(i == 0 || errno != EINTR)
  409.       kill(getpid(), SIGHUP);                /* eof or bad error */
  410.  
  411.     return(NODATA);                    /* acceptable error */
  412. }
  413.  
  414.  
  415. #if    TYPEAH
  416. /* 
  417.  * typahead - Check to see if any characters are already in the
  418.  *          keyboard buffer
  419.  */
  420. typahead()
  421. {
  422.     int x;    /* holds # of pending chars */
  423.  
  424.     return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
  425. }
  426. #endif
  427.  
  428.  
  429. /*
  430.  * GetKey - Read in a key.
  431.  * Do the standard keyboard preprocessing. Convert the keys to the internal
  432.  * character set.  Resolves escape sequences and returns no-op if global
  433.  * timeout value exceeded.
  434.  */
  435. GetKey()
  436. {
  437.     int    c, status, cc;
  438.  
  439.     if(timeout){
  440.     /*
  441.      * simple use of select/poll here to handle requested timeouts
  442.      * while waiting for keyboard input...
  443.      */
  444. #if    defined(ptx) || defined(sv4)
  445.     struct pollfd pollfd;
  446.     int    rv;
  447.  
  448.     pollfd.fd     = 0;
  449.     pollfd.events = POLLIN;
  450.     while((rv = poll(&pollfd, 1, timeout * 1000)) < 0 && errno == EAGAIN)
  451.       ;
  452. #else
  453.     struct timeval ts;
  454.     fd_set readfds;
  455.     int    rv;
  456.  
  457.     FD_ZERO(&readfds);        /* blank out all bits */
  458.     FD_SET(0, &readfds);        /* set stdin's bit */
  459.     ts.tv_sec  = timeout;        /* set the timeout */
  460.     ts.tv_usec = 0;
  461.  
  462.     rv = select(1, &readfds, 0, &readfds, &ts); /* read stdin */
  463. #endif
  464.     if(rv < 0){
  465.         if(errno == EINTR){        /* interrupted? */
  466.         return(NODATA);        /* return like we timed out */
  467.         }
  468.         else{
  469.         emlwrite("\007Problem reading from keyboard!", NULL);
  470.         kill(getpid(), SIGHUP);    /* Bomb out (saving our work)! */
  471.         }
  472.     }
  473.     else if(rv == 0)
  474.       return(NODATA);        /* we really did time out */
  475.     }
  476.  
  477.     switch(status = kbseq(pico_kbesc, term.t_getchar, &c)){
  478.       case 0:     /* regular character */
  479.     break;
  480.  
  481.       case K_DOUBLE_ESC:
  482.     c = (*term.t_getchar)();
  483.  
  484.     if(isdigit(c)){
  485.         int n = 0, i = c - '0';
  486.  
  487.         if(!strchr("012", c))
  488.           return(c);        /* bogus literal char value */
  489.  
  490.         while(n++ < 2){
  491.         c = (*term.t_getchar)();
  492.                     /* remember Horner? */
  493.         if(!isdigit(c) || (n == 1 && i == 2 && c > '5'))
  494.           return(c);        /* bogus literal char value */
  495.  
  496.         i = (i * 10) + (c - '0');
  497.         }
  498.  
  499.         c = i;
  500.         break;
  501.     }
  502.     else{
  503.         if(islower(c))    /* canonicalize c */
  504.           c = toupper(c);
  505.  
  506.         return((isalpha(c) || c == '@' || (c >= '[' && c <= '_'))
  507.            ? (CTRL | c) : c);
  508.     }
  509.  
  510. #ifdef MOUSE
  511.       case K_XTERM_MOUSE:
  512.     {
  513.         static int down = 0;
  514.         int        x, y, button;
  515.         unsigned   ch;
  516.  
  517.         button = (*term.t_getchar)() & 0x03;
  518.         c = (*term.t_getchar)();
  519.         x = c - '!';
  520.         c = (*term.t_getchar)();
  521.         y = c - '!';
  522.         if(button == 0){
  523.         down = 1;
  524.         if(checkmouse(&ch, 1, x, y))
  525.           return(ch);
  526.         }
  527.         else if(down && button == 3){
  528.         down = 0;
  529.         if(checkmouse(&ch, 0, x, y))
  530.           return(ch);
  531.         }
  532.  
  533.         return(NODATA);
  534.     }
  535.  
  536.     break;
  537. #endif /* MOUSE */
  538.  
  539.       case  K_PAD_UP        :
  540.       case  K_PAD_DOWN        :
  541.       case  K_PAD_RIGHT        :
  542.       case  K_PAD_LEFT        :
  543.       case  K_PAD_PREVPAGE    :
  544.       case  K_PAD_NEXTPAGE    :
  545.       case  K_PAD_HOME        :
  546.       case  K_PAD_END        :
  547.       case  K_PAD_DELETE    :
  548.       case F1  :
  549.       case F2  :
  550.       case F3  :
  551.       case F4  :
  552.       case F5  :
  553.       case F6  :
  554.       case F7  :
  555.       case F8  :
  556.       case F9  :
  557.       case F10 :
  558.       case F11 :
  559.       case F12 :
  560.     return(status);
  561.  
  562.       case K_SWALLOW_TIL_Z:
  563.     status = NODATA;
  564.       case K_SWALLOW_UP:
  565.       case K_SWALLOW_DOWN:
  566.       case K_SWALLOW_LEFT:
  567.       case K_SWALLOW_RIGHT:
  568.     for(cc = 0; cc != 'z' && cc != '~';)
  569.       cc = (*term.t_getchar)() & 0x7f;
  570.  
  571.     return(status);
  572.     break;
  573.  
  574.       case K_KERMIT:
  575.     for(cc = 0; cc != '\033' || ((*term.t_getchar)() & 0x7f) != '\\';)
  576.       cc = (*term.t_getchar)() & 0x7f;
  577.  
  578.     c = NODATA;
  579.     break;
  580.  
  581.       case BADESC:
  582.     (*term.t_beep)();
  583.     return(status);
  584.  
  585.       default:                /* punt the whole thing    */
  586.     (*term.t_beep)();
  587.     break;
  588.     }
  589.  
  590.     if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
  591.       c = CTRL | (c+'@');
  592.  
  593.     return (c);
  594. }
  595.  
  596.  
  597.  
  598. /* 
  599.  * kbseq - looks at an escape sequence coming from the keyboard and 
  600.  *         compares it to a trie of known keyboard escape sequences, and
  601.  *         performs the function bound to the escape sequence.
  602.  * 
  603.  *         returns: BADESC, the escaped function, or 0 if a regular char.
  604.  */
  605. kbseq(trie, getcfunc, c)
  606.     KBESC_T *trie;
  607.     int    (*getcfunc)();
  608.     int        *c;
  609. {
  610.     register char     b;
  611.     register int      first = 1;
  612.     register KBESC_T *current = trie;
  613.  
  614.     if(trie == NULL)                /* bag it */
  615.       return(BADESC);
  616.  
  617.     while(1){
  618.     b = *c = (*getcfunc)();
  619.  
  620.     while(current->value != b){
  621.         if(current->left == NULL){        /* NO MATCH */
  622.         if(first)
  623.           return(0);            /* regular char */
  624.         else
  625.           return(BADESC);
  626.         }
  627.         current = current->left;
  628.     }
  629.  
  630.     if(current->down == NULL)        /* match!!!*/
  631.       return(current->func);
  632.     else
  633.       current = current->down;
  634.  
  635.     first = 0;
  636.     }
  637. }
  638.  
  639.  
  640.  
  641. #define    newnode()    (KBESC_T *)malloc(sizeof(KBESC_T))
  642. /*
  643.  * kpinsert - insert a keystroke escape sequence into the global search
  644.  *          structure.
  645.  */
  646. void
  647. kpinsert(trie, kstr, kval)
  648.     KBESC_T **trie;
  649.     char     *kstr;
  650.     int       kval;
  651. {
  652.     register    char    *buf;
  653.     register    KBESC_T *temp;
  654.     register    KBESC_T *trail;
  655.  
  656.     if(kstr == NULL)
  657.       return;
  658.  
  659. #ifndef TERMCAP_WINS
  660.     /*
  661.      * Don't allow escape sequences that don't start with ESC unless
  662.      * TERMCAP_WINS is defined.  This is to protect against mistakes
  663.      * in termcap files.
  664.      */
  665.     if(*kstr != '\033')
  666.       return;
  667. #endif /* !TERMCAP_WINS */
  668.  
  669.     temp = trail = *trie;
  670.     buf = kstr;
  671.  
  672.     for(;;){
  673.     if(temp == NULL){
  674.         temp = newnode();
  675.         temp->value = *buf;
  676.         temp->func = 0;
  677.         temp->left = NULL;
  678.         temp->down = NULL;
  679.         if(*trie == NULL)
  680.           *trie = temp;
  681.         else
  682.           trail->down = temp;
  683.     }
  684.     else{                /* first entry */
  685.         while((temp != NULL) && (temp->value != *buf)){
  686.         trail = temp;
  687.         temp = temp->left;
  688.         }
  689.  
  690.         if(temp == NULL){   /* add new val */
  691.         temp = newnode();
  692.         temp->value = *buf;
  693.         temp->func = 0;
  694.         temp->left = NULL;
  695.         temp->down = NULL;
  696.         trail->left = temp;
  697.         }
  698.     }
  699.  
  700.     if(*(++buf) == '\0')
  701.       break;
  702.     else{
  703.         /*
  704.          * Ignore attempt to overwrite shorter existing escape sequence.
  705.          * That means that sequences with higher priority should be
  706.          * set up first, so if we want termcap sequences to override
  707.          * hardwired sequences, put the kpinsert calls for the
  708.          * termcap sequences first.  (That's what you get if you define
  709.          * TERMCAP_WINS.)
  710.          */
  711.         if(temp->func != 0)
  712.           return;
  713.  
  714.         trail = temp;
  715.         temp = temp->down;
  716.     }
  717.     }
  718.     
  719.     /*
  720.      * Ignore attempt to overwrite longer sequences we are a prefix
  721.      * of (down != NULL) and exact same sequence (func != 0).
  722.      */
  723.     if(temp != NULL && temp->down == NULL && temp->func == 0)
  724.       temp->func = kval;
  725. }
  726.  
  727.  
  728.  
  729. /*
  730.  * kbdestroy() - kills the key pad function key search tree
  731.  *         and frees all lines associated with it
  732.  */
  733. void
  734. kbdestroy(kb)
  735.     KBESC_T *kb;
  736. {
  737.     if(kb){
  738.     kbdestroy(kb->left);
  739.     kbdestroy(kb->down);
  740.     free((char *) kb);
  741.     }
  742. }
  743.  
  744.  
  745.  
  746. /*
  747.  * alt_editor - fork off an alternate editor for mail message composition 
  748.  *              if one is configured and passed from pine.  If not, only
  749.  *              ask for the editor if advanced user flag is set, and 
  750.  *              suggest environment's EDITOR value as default.
  751.  */
  752. alt_editor(f, n)
  753. {
  754.     char   eb[NLINE];                /* buf holding edit command */
  755.     char   *fn;                    /* tmp holder for file name */
  756.     char   *cp;
  757.     char   *args[MAXARGS];            /* ptrs into edit command */
  758.     char   result[128];                /* result string */
  759.     int       child, pid, i, done = 0;
  760.     long   l;
  761.     FILE   *p;
  762.     SIGTYPE (*ohup)(), (*oint)(), (*osize)(), (*ostop)(), (*ostart)();
  763. #if    defined(POSIX) || defined(sv3) || defined(COHERENT) || defined(isc) || defined(neb)
  764.     int    stat;
  765. #ifndef    WIFEXITED
  766. #define    WIFEXITED(X)    (!((X) & 0xff))        /* low bits, child killed */
  767. #endif
  768. #ifndef    WEXITSTATUS
  769. #define    WEXITSTATUS(X)    ((X) >> 8)        /* high bits, exit value */
  770. #endif
  771. #else
  772.     union  wait stat;
  773. #ifndef    WIFEXITED
  774. #define    WIFEXITED(X)    (!(X).w_termsig)    /* nonzero if child killed */
  775. #endif
  776. #ifndef    WEXITSTATUS
  777. #define    WEXITSTATUS(X)    X.w_retcode        /* child's exit value */
  778. #endif
  779. #endif
  780.  
  781.     if(Pmaster == NULL)
  782.       return(-1);
  783.  
  784.     if(gmode&MDSCUR){
  785.     emlwrite("Alternate %s not available in restricted mode",
  786.          f ? "speller" : "editor");
  787.     return(-1);
  788.     }
  789.  
  790.     strcpy(result, "Alternate %s complete.");
  791.  
  792.     if(f){
  793.     if(alt_speller)
  794.       strcpy(eb, alt_speller);
  795.     else
  796.       return(-1);
  797.     }
  798.     else{
  799.     if(Pmaster->alt_ed == NULL){
  800.         if(!(gmode&MDADVN)){
  801.         emlwrite("\007Unknown Command",NULL);
  802.         return(-1);
  803.         }
  804.  
  805.         if(getenv("EDITOR"))
  806.           strcpy(eb, (char *)getenv("EDITOR"));
  807.         else
  808.           *eb = '\0';
  809.  
  810.         while(!done){
  811.         pid = mlreplyd("Which alternate editor ? ", eb,
  812.                    NLINE, QDEFLT, NULL);
  813.         switch(pid){
  814.           case ABORT:
  815.             return(-1);
  816.           case HELPCH:
  817.             emlwrite("no alternate editor help yet", NULL);
  818.  
  819. /* take sleep and break out after there's help */
  820.             sleep(3);
  821.             break;
  822.           case (CTRL|'L'):
  823.             sgarbf = TRUE;
  824.             update();
  825.             break;
  826.           case TRUE:
  827.           case FALSE:            /* does editor exist ? */
  828.             if(*eb == '\0'){        /* leave silently? */
  829.             mlerase();
  830.             return(-1);
  831.             }
  832.  
  833.             done++;
  834.             break;
  835.             default:
  836.             break;
  837.         }
  838.         }
  839.     }
  840.     else
  841.       strcpy(eb, Pmaster->alt_ed);
  842.     }
  843.  
  844.     if((fn=writetmp(0, 1)) == NULL){        /* get temp file */
  845.     emlwrite("Problem writing temp file for alt editor", NULL);
  846.     return(-1);
  847.     }
  848.  
  849.     strcat(eb, " ");
  850.     strcat(eb, fn);
  851.  
  852.     cp = eb;
  853.     for(i=0; *cp != '\0';i++){            /* build args array */
  854.     if(i < MAXARGS){
  855.         args[i] = NULL;            /* in case we break out */
  856.     }
  857.     else{
  858.         emlwrite("Too many args for command!", NULL);
  859.         return(-1);
  860.     }
  861.  
  862.     while(isspace(*cp))
  863.       if(*cp != '\0')
  864.         cp++;
  865.       else
  866.         break;
  867.  
  868.     args[i] = cp;
  869.  
  870.     while(!isspace(*cp))
  871.       if(*cp != '\0')
  872.         cp++;
  873.       else
  874.         break;
  875.  
  876.     if(*cp != '\0')
  877.       *cp++ = '\0';
  878.     }
  879.  
  880.     args[i] = NULL;
  881.  
  882.     for(i = 0; i <= ((Pmaster) ? term.t_nrow : term.t_nrow - 1); i++){
  883.     movecursor(i, 0);
  884.     if(!i){
  885.         fputs("Invoking alternate ", stdout);
  886.         fputs(f ? "speller..." : "editor...", stdout);
  887.     }
  888.  
  889.     peeol();
  890.     }
  891.  
  892.     (*term.t_flush)();
  893.     if(Pmaster)
  894.       (*Pmaster->raw_io)(0);            /* turn OFF raw mode */
  895.  
  896. #ifdef    SIGCHLD
  897.     if(Pmaster){
  898.     /*
  899.      * The idea here is to keep any mail connections our caller
  900.      * may have open while our child's out running around...
  901.      */
  902.     pico_child_done = pico_child_jmp_ok = 0;
  903.     (void) signal(SIGCHLD,  child_handler);
  904.     }
  905. #endif
  906.  
  907.     if((child = fork()) > 0){        /* wait for the child to finish */
  908.     ohup = signal(SIGHUP, SIG_IGN);    /* ignore signals for now */
  909.     oint = signal(SIGINT, SIG_IGN);
  910. #ifdef    TIOCGWINSZ
  911.         osize = signal(SIGWINCH, SIG_IGN);
  912. #endif
  913.  
  914. #ifdef    SIGCHLD
  915.     if(Pmaster){
  916.         while(!pico_child_done){
  917.         (*Pmaster->newmail)(0, 0);
  918.         if(!pico_child_done)
  919.           if(setjmp(pico_child_state) == 0){
  920.               pico_child_jmp_ok = 1;
  921.               sleep(600);
  922.           }
  923.  
  924.         pico_child_jmp_ok = 0;
  925.         }
  926.     }
  927. #endif
  928.  
  929.     while((pid = (int) wait(&stat)) != child)
  930.       ;
  931.  
  932.     signal(SIGHUP, ohup);    /* restore signals */
  933.     signal(SIGINT, oint);
  934. #ifdef    TIOCGWINSZ
  935.         signal(SIGWINCH, osize);
  936. #endif
  937.  
  938.     /*
  939.      * Report child's unnatural or unhappy exit...
  940.      */
  941.     if(WIFEXITED(stat) && WEXITSTATUS(stat) == 0)
  942.       strcpy(result, "Alternate %s done");
  943.     else
  944.       sprintf(result, "Alternate %%s abnormally terminated (%d)",
  945.           WIFEXITED(stat) ? WEXITSTATUS(stat) : -1);
  946.     }
  947.     else if(child == 0){        /* spawn editor */
  948.     signal(SIGHUP, SIG_DFL);    /* let editor handle signals */
  949.     signal(SIGINT, SIG_DFL);
  950. #ifdef    TIOCGWINSZ
  951.         signal(SIGWINCH, SIG_DFL);
  952. #endif
  953. #ifdef    SIGCHLD
  954.     (void) signal(SIGCHLD,  SIG_DFL);
  955. #endif
  956.     if(execvp(args[0], args) < 0)
  957.       exit(-1);
  958.     }
  959.     else                /* error! */
  960.       sprintf(result, "\007Can't fork %%s: %s", errstr(errno));
  961.  
  962. #ifdef    SIGCHLD
  963.     (void) signal(SIGCHLD,  SIG_DFL);
  964. #endif
  965.  
  966.     if(Pmaster)
  967.       (*Pmaster->raw_io)(1);        /* turn ON raw mode */
  968.  
  969.     /*
  970.      * replace edited text with new text 
  971.      */
  972.     curbp->b_flag &= ~BFCHG;        /* make sure old text gets blasted */
  973.     readin(fn, 0);            /* read new text overwriting old */
  974.     unlink(fn);                /* blast temp file */
  975.     curbp->b_flag |= BFCHG;        /* mark dirty for packbuf() */
  976.     ttopen();                /* reset the signals */
  977.     refresh(0, 1);            /* redraw */
  978.     update();
  979.     emlwrite(result, f ? "speller" : "editor");
  980.     return(0);
  981. }
  982.  
  983.  
  984.  
  985. /*
  986.  *  bktoshell - suspend and wait to be woken up
  987.  */
  988. bktoshell()        /* suspend MicroEMACS and wait to wake up */
  989. {
  990. #ifdef    SIGTSTP
  991.     int pid;
  992.  
  993.     if(!(gmode&MDSSPD)){
  994.     emlwrite("\007Unknown command: ^Z", NULL);
  995.     return;
  996.     }
  997.  
  998.     if(gmode&MDSPWN){
  999.     char *shell;
  1000.  
  1001.     if(Pmaster){
  1002.         (*Pmaster->raw_io)(0);    /* actually in pine source */
  1003.  
  1004.         movecursor(0, 0);
  1005.         (*term.t_eeop)();
  1006.         printf("\n\n\nUse \"exit\" to return to Pine\n");
  1007.     }
  1008.     else{
  1009.         vttidy();
  1010.         movecursor(0, 0);
  1011.         (*term.t_eeop)();
  1012.         printf("\n\n\nUse \"exit\" to return to Pico\n");
  1013.     }
  1014.  
  1015.     system((shell = (char *)getenv("SHELL")) ? shell : "/bin/csh");
  1016.     rtfrmshell();            /* fixup tty */
  1017.     }
  1018.     else {
  1019.     if(Pmaster){
  1020.         (*Pmaster->raw_io)(0);    /* actually in pine source */
  1021.  
  1022.         movecursor(term.t_nrow, 0);
  1023.         printf("\n\n\nUse \"fg\" to return to Pine\n");
  1024.     }
  1025.     else{
  1026.         movecursor(term.t_nrow-1, 0);
  1027.         peeol();
  1028.         movecursor(term.t_nrow, 0);
  1029.         peeol();
  1030.         movecursor(term.t_nrow, 0);
  1031.         printf("\n\n\nUse \"fg\" to return to Pico\n");
  1032.         ttclose();
  1033.     }
  1034.  
  1035.     movecursor(term.t_nrow, 0);
  1036.     peeol();
  1037.     (*term.t_flush)();
  1038.  
  1039.     signal(SIGCONT, rtfrmshell);    /* prepare to restart */
  1040.     signal(SIGTSTP, SIG_DFL);            /* prepare to stop */
  1041.     kill(0, SIGTSTP);
  1042.     }
  1043. #endif
  1044. }
  1045.  
  1046.  
  1047. /* 
  1048.  * rtfrmshell - back from shell, fix modes and return
  1049.  */
  1050. SIGTYPE
  1051. rtfrmshell()
  1052. {
  1053. #ifdef    SIGCONT
  1054.     signal(SIGCONT, SIG_DFL);
  1055.  
  1056.     if(Pmaster){
  1057.     (*Pmaster->raw_io)(1);            /* actually in pine source */
  1058.     (*Pmaster->keybinit)(gmode&MDFKEY);    /* using f-keys? */
  1059.     }
  1060.  
  1061.     ttopen();
  1062.     ttresize();
  1063.     refresh(0, 1);
  1064. #endif
  1065. }
  1066.  
  1067.  
  1068.  
  1069. /*
  1070.  * do_hup_signal - jump back in the stack to where we can handle this
  1071.  */
  1072. SIGTYPE
  1073. do_hup_signal()
  1074. {
  1075.     signal(SIGHUP,  SIG_IGN);            /* ignore further SIGHUP's */
  1076.     signal(SIGTERM, SIG_IGN);            /* ignore further SIGTERM's */
  1077.     if(Pmaster){
  1078.     extern jmp_buf finstate;
  1079.  
  1080.     longjmp(finstate, COMP_GOTHUP);
  1081.     }
  1082.     else{
  1083.     /*
  1084.      * if we've been interrupted and the buffer is changed,
  1085.      * save it...
  1086.      */
  1087.     if(anycb() == TRUE){            /* time to save */
  1088.         if(curbp->b_fname[0] == '\0'){    /* name it */
  1089.         strcpy(curbp->b_fname, "pico.save");
  1090.         }
  1091.         else{
  1092.         strcat(curbp->b_fname, ".save");
  1093.         }
  1094.         writeout(curbp->b_fname);
  1095.     }
  1096.     vttidy();
  1097.     exit(1);
  1098.     }
  1099. }
  1100.  
  1101.  
  1102. /*
  1103.  * big bitmap of ASCII characters allowed in a file name
  1104.  * (needs reworking for other char sets)
  1105.  */
  1106. unsigned char okinfname[32] = {
  1107.       0,    0,             /* ^@ - ^G, ^H - ^O  */
  1108.       0,    0,            /* ^P - ^W, ^X - ^_  */
  1109.       0x80, 0x17,        /* SP - ' ,  ( - /   */
  1110.       0xff, 0xc0,        /*  0 - 7 ,  8 - ?   */
  1111.       0x7f, 0xff,        /*  @ - G ,  H - O   */
  1112.       0xff, 0xe1,        /*  P - W ,  X - _   */
  1113.       0x7f, 0xff,        /*  ` - g ,  h - o   */
  1114.       0xff, 0xf6,        /*  p - w ,  x - DEL */
  1115.       0,    0,             /*  > DEL   */
  1116.       0,    0,            /*  > DEL   */
  1117.       0,    0,             /*  > DEL   */
  1118.       0,    0,             /*  > DEL   */
  1119.       0,    0             /*  > DEL   */
  1120. };
  1121.  
  1122.  
  1123. /*
  1124.  * fallowc - returns TRUE if c is allowable in filenames, FALSE otw
  1125.  */
  1126. fallowc(c)
  1127. char c;
  1128. {
  1129.     return(okinfname[c>>3] & 0x80>>(c&7));
  1130. }
  1131.  
  1132.  
  1133. /*
  1134.  * fexist - returns TRUE if the file exists with mode passed in m, 
  1135.  *          FALSE otherwise.  By side effect returns length of file in l
  1136.  */
  1137. fexist(file, m, l)
  1138. char *file;
  1139. char *m;            /* files mode: r,w,rw,t or x */
  1140. long *l;            /* t means use lstat         */
  1141. {
  1142.     struct stat    sbuf;
  1143.     extern int lstat();
  1144.     int        (*stat_f)() = (m && *m == 't') ? lstat : stat;
  1145.     int accessible;
  1146. /* define them here so we don't have to worry about what to include */
  1147. #define EXECUTE_ACCESS  01
  1148. #define WRITE_ACCESS    02
  1149. #define READ_ACCESS     04
  1150.  
  1151.     if(l)
  1152.       *l = 0L;
  1153.     
  1154.     if((*stat_f)(file, &sbuf) < 0){
  1155.     switch(errno){
  1156.       case ENOENT :                /* File not found */
  1157.         return(FIOFNF);
  1158. #ifdef    ENAMETOOLONG
  1159.       case ENAMETOOLONG :            /* Name is too long */
  1160.         return(FIOLNG);
  1161. #endif
  1162.       case EACCES :                /* File not found */
  1163.         return(FIOPER);
  1164.       default:                /* Some other error */
  1165.         return(FIOERR);
  1166.     }
  1167.     }
  1168.  
  1169.     if(l)
  1170.       *l = sbuf.st_size;
  1171.  
  1172.     if((sbuf.st_mode&S_IFMT) == S_IFDIR)
  1173.       return(FIODIR);
  1174.     else if(*m == 't'){
  1175.     struct stat    sbuf2;
  1176.  
  1177.     /*
  1178.      * If it is a symbolic link pointing to a directory, treat
  1179.      * it like it is a directory, not a link.
  1180.      */
  1181.     if((sbuf.st_mode&S_IFMT) == S_IFLNK){
  1182.         if(stat(file, &sbuf2) < 0){
  1183.         switch(errno){
  1184.           case ENOENT :                /* File not found */
  1185.             return(FIOSYM);            /* call it a link */
  1186. #ifdef    ENAMETOOLONG
  1187.           case ENAMETOOLONG :            /* Name is too long */
  1188.             return(FIOLNG);
  1189. #endif
  1190.           case EACCES :                /* File not found */
  1191.             return(FIOPER);
  1192.           default:                /* Some other error */
  1193.             return(FIOERR);
  1194.         }
  1195.         }
  1196.  
  1197.         if((sbuf2.st_mode&S_IFMT) == S_IFDIR)
  1198.           return(FIODIR);
  1199.     }
  1200.  
  1201.     return(((sbuf.st_mode&S_IFMT) == S_IFLNK) ? FIOSYM : FIOSUC);
  1202.     }
  1203.  
  1204.     if(*m == 'r'){                /* read access? */
  1205.     if(*(m+1) == 'w')            /* and write access? */
  1206.       return((access(file,READ_ACCESS)==0)
  1207.          ? (access(file,WRITE_ACCESS)==0)
  1208.             ? FIOSUC
  1209.             : FIONWT
  1210.          : FIONRD);
  1211.     else if(!*(m+1))            /* just read access? */
  1212.       return((access(file,READ_ACCESS)==0) ? FIOSUC : FIONRD);
  1213.     }
  1214.     else if(*m == 'w' && !*(m+1))        /* write access? */
  1215.       return((access(file,WRITE_ACCESS)==0) ? FIOSUC : FIONWT);
  1216.     else if(*m == 'x' && !*(m+1))        /* execute access? */
  1217.       return((access(file,EXECUTE_ACCESS)==0) ? FIOSUC : FIONEX);
  1218.     return(FIOERR);                /* bad m arg */
  1219. }
  1220.  
  1221.  
  1222. /*
  1223.  * isdir - returns true if fn is a readable directory, false otherwise
  1224.  *         silent on errors (we'll let someone else notice the problem;)).
  1225.  */
  1226. isdir(fn, l)
  1227. char *fn;
  1228. long *l;
  1229. {
  1230.     struct stat sbuf;
  1231.  
  1232.     if(l)
  1233.       *l = 0;
  1234.  
  1235.     if(stat(fn, &sbuf) < 0)
  1236.       return(0);
  1237.  
  1238.     if(l)
  1239.       *l = sbuf.st_size;
  1240.     return((sbuf.st_mode&S_IFMT) == S_IFDIR);
  1241. }
  1242.  
  1243.  
  1244. #if    defined(bsd) || defined(nxt) || defined(dyn)
  1245. /*
  1246.  * getcwd - NeXT uses getwd()
  1247.  */
  1248. char *
  1249. getcwd(pth, len)
  1250. char *pth;
  1251. int   len;
  1252. {
  1253.     extern char *getwd();
  1254.  
  1255.     return(getwd(pth));
  1256. }
  1257. #endif
  1258.  
  1259.  
  1260. /*
  1261.  * gethomedir - returns the users home directory
  1262.  *              Note: home is malloc'd for life of pico
  1263.  */
  1264. char *
  1265. gethomedir(l)
  1266. int *l;
  1267. {
  1268.     static char *home = NULL;
  1269.     static short hlen = 0;
  1270.  
  1271.     if(home == NULL){
  1272.     char buf[NLINE];
  1273.     strcpy(buf, "~");
  1274.     fixpath(buf, NLINE);        /* let fixpath do the work! */
  1275.     hlen = strlen(buf);
  1276.     if((home = (char *)malloc((hlen + 1) * sizeof(char))) == NULL){
  1277.         emlwrite("Problem allocating space for home dir", NULL);
  1278.         return(0);
  1279.     }
  1280.  
  1281.     strcpy(home, buf);
  1282.     }
  1283.  
  1284.     if(l)
  1285.       *l = hlen;
  1286.  
  1287.     return(home);
  1288. }
  1289.  
  1290.  
  1291. /*
  1292.  * homeless - returns true if given file does not reside in the current
  1293.  *            user's home directory tree. 
  1294.  */
  1295. homeless(f)
  1296. char *f;
  1297. {
  1298.     char *home;
  1299.     int   len;
  1300.  
  1301.     home = gethomedir(&len);
  1302.     return(strncmp(home, f, len));
  1303. }
  1304.  
  1305.  
  1306.  
  1307. /*
  1308.  * errstr - return system error string corresponding to given errno
  1309.  *          Note: strerror() is not provided on all systems, so it's 
  1310.  *          done here once and for all.
  1311.  */
  1312. char *
  1313. errstr(err)
  1314. int err;
  1315. {
  1316. #ifndef    neb
  1317.     extern char *sys_errlist[];
  1318. #endif
  1319.     extern int  sys_nerr;
  1320.  
  1321.     return((err >= 0 && err < sys_nerr) ? sys_errlist[err] : NULL);
  1322. }
  1323.  
  1324.  
  1325.  
  1326. /*
  1327.  * getfnames - return all file names in the given directory in a single 
  1328.  *             malloc'd string.  n contains the number of names
  1329.  */
  1330. char *
  1331. getfnames(dn, pat, n, e)
  1332. char *dn, *pat, *e;
  1333. int  *n;
  1334. {
  1335.     long           l;
  1336.     char          *names, *np, *p;
  1337.     struct stat    sbuf;
  1338. #if    defined(ct)
  1339.     FILE          *dirp;
  1340.     char           fn[DIRSIZ+1];
  1341. #else
  1342.     DIR           *dirp;            /* opened directory */
  1343. #endif
  1344. #if    defined(POSIX) || defined(aix) || defined(COHERENT) || defined(isc) || defined(sv3) || defined(asv)
  1345.     struct dirent *dp;
  1346. #else
  1347.     struct direct *dp;
  1348. #endif
  1349.  
  1350.     *n = 0;
  1351.  
  1352.     if(stat(dn, &sbuf) < 0){
  1353.     switch(errno){
  1354.       case ENOENT :                /* File not found */
  1355.         if(e)
  1356.           sprintf(e, "\007File not found: \"%s\"", dn);
  1357.  
  1358.         break;
  1359. #ifdef    ENAMETOOLONG
  1360.       case ENAMETOOLONG :            /* Name is too long */
  1361.         if(e)
  1362.           sprintf(e, "\007File name too long: \"%s\"", dn);
  1363.  
  1364.         break;
  1365. #endif
  1366.       default:                /* Some other error */
  1367.         if(e)
  1368.           sprintf(e, "\007Error getting file info: \"%s\"", dn);
  1369.  
  1370.         break;
  1371.     }
  1372.     return(NULL);
  1373.     } 
  1374.     else{
  1375.     l = sbuf.st_size;
  1376.     if((sbuf.st_mode&S_IFMT) != S_IFDIR){
  1377.         if(e)
  1378.           sprintf(e, "\007Not a directory: \"%s\"", dn);
  1379.  
  1380.         return(NULL);
  1381.     }
  1382.     }
  1383.  
  1384.     if((names=(char *)malloc(sizeof(char)*l)) == NULL){
  1385.     if(e)
  1386.       sprintf(e, "\007Can't malloc space for file names", NULL);
  1387.  
  1388.     return(NULL);
  1389.     }
  1390.  
  1391.     errno = 0;
  1392.     if((dirp=opendir(dn)) == NULL){
  1393.     if(e)
  1394.       sprintf(e, "\007Can't open \"%s\": %s", dn, errstr(errno));
  1395.  
  1396.     free((char *)names);
  1397.     return(NULL);
  1398.     }
  1399.  
  1400.     np = names;
  1401.  
  1402. #if    defined(ct)
  1403.     while(fread(&dp, sizeof(struct direct), 1, dirp) > 0) {
  1404.     /* skip empty slots with inode of 0 */
  1405.     if(dp.d_ino == 0)
  1406.          continue;
  1407.     (*n)++;                     /* count the number of active slots */
  1408.     (void)strncpy(fn, dp.d_name, DIRSIZ);
  1409.     fn[14] = '\0';
  1410.     p = fn;
  1411.     while((*np++ = *p++) != '\0')
  1412.       ;
  1413.     }
  1414. #else
  1415.     while((dp = readdir(dirp)) != NULL)
  1416.       if(!pat || !*pat || !strncmp(dp->d_name, pat, strlen(pat))){
  1417.       (*n)++;
  1418.       p = dp->d_name;
  1419.       while(*np++ = *p++)
  1420.         ;
  1421.       }
  1422. #endif
  1423.  
  1424.     closedir(dirp);                    /* shut down */
  1425.     return(names);
  1426. }
  1427.  
  1428.  
  1429. /*
  1430.  * fioperr - given the error number and file name, display error
  1431.  */
  1432. void
  1433. fioperr(e, f)
  1434. int  e;
  1435. char *f;
  1436. {
  1437.     switch(e){
  1438.       case FIOFNF:                /* File not found */
  1439.     emlwrite("\007File \"%s\" not found", f);
  1440.     break;
  1441.       case FIOEOF:                /* end of file */
  1442.     emlwrite("\007End of file \"%s\" reached", f);
  1443.     break;
  1444.       case FIOLNG:                /* name too long */
  1445.     emlwrite("\007File name \"%s\" too long", f);
  1446.     break;
  1447.       case FIODIR:                /* file is a directory */
  1448.     emlwrite("\007File \"%s\" is a directory", f);
  1449.     break;
  1450.       case FIONWT:
  1451.     emlwrite("\007Write permission denied: %s", f);
  1452.     break;
  1453.       case FIONRD:
  1454.     emlwrite("\007Read permission denied: %s", f);
  1455.     break;
  1456.       case FIOPER:
  1457.     emlwrite("\007Permission denied: %s", f);
  1458.     break;
  1459.       case FIONEX:
  1460.     emlwrite("\007Execute permission denied: %s", f);
  1461.     break;
  1462.       default:
  1463.     emlwrite("\007File I/O error: %s", f);
  1464.     }
  1465. }
  1466.  
  1467.  
  1468.  
  1469. /*
  1470.  * pfnexpand - pico's function to expand the given file name if there is 
  1471.  *           a leading '~'
  1472.  */
  1473. char *pfnexpand(fn, len)
  1474. char *fn;
  1475. int  len;
  1476. {
  1477.     struct passwd *pw;
  1478.     register char *x, *y, *z;
  1479.     char *home = NULL;
  1480.     char name[20];
  1481.     
  1482.     if(*fn == '~') {
  1483.         for(x = fn+1, y = name; *x != '/' && *x != '\0'; *y++ = *x++)
  1484.       ;
  1485.  
  1486.         *y = '\0';
  1487.         if(x == fn + 1){            /* ~/ */
  1488.         if (!(home = (char *) getenv("HOME")))
  1489.           if (pw = getpwuid(geteuid()))
  1490.         home = pw->pw_dir;
  1491.     }
  1492.     else if(*name){                /* ~username/ */
  1493.         if(pw = getpwnam(name))
  1494.           home = pw->pw_dir;
  1495.     }
  1496.  
  1497.         if(!home || (strlen(home) + strlen(fn) > len))
  1498.       return(NULL);
  1499.  
  1500.     /* make room for expanded path */
  1501.     for(z = x + strlen(x), y = fn + strlen(x) + strlen(home);
  1502.         z >= x;
  1503.         *y-- = *z--)
  1504.       ;
  1505.  
  1506.     /* and insert the expanded address */
  1507.     for(x = fn, y = home; *y != '\0'; *x++ = *y++)
  1508.       ;
  1509.     }
  1510.     return(fn);
  1511. }
  1512.  
  1513.  
  1514.  
  1515. /*
  1516.  * fixpath - make the given pathname into an absolute path
  1517.  */
  1518. fixpath(name, len)
  1519. char *name;
  1520. int  len;
  1521. {
  1522.     register char *shft;
  1523.  
  1524.     /* filenames relative to ~ */
  1525.     if(!((name[0] == '/')
  1526.           || (name[0] == '.'
  1527.               && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))))){
  1528.     if(Pmaster && !(gmode&MDCURDIR)
  1529.                    && (*name != '~' && strlen(name)+2 <= len)){
  1530.  
  1531.         if(gmode&MDTREE && strlen(name)+strlen(opertree)+1 <= len){
  1532.         int i, off = strlen(opertree);
  1533.  
  1534.         for(shft = strchr(name, '\0'); shft >= name; shft--)
  1535.           shft[off] = *shft;
  1536.         
  1537.         off--;
  1538.         for(shft=name, i=0; i < off; i++)
  1539.           shft[i] = opertree[i];
  1540.         }
  1541.         else{
  1542.         for(shft = strchr(name, '\0'); shft >= name; shft--)
  1543.           shft[2] = *shft;
  1544.  
  1545.         name[0] = '~';
  1546.         name[1] = '/';
  1547.         }
  1548.     }
  1549.  
  1550.     pfnexpand(name, len);
  1551.     }
  1552. }
  1553.  
  1554.  
  1555. /*
  1556.  * compresspath - given a base path and an additional directory, collapse
  1557.  *                ".." and "." elements and return absolute path (appending
  1558.  *                base if necessary).  
  1559.  *
  1560.  *                returns  1 if OK, 
  1561.  *                         0 if there's a problem
  1562.  *                         new path, by side effect, if things went OK
  1563.  */
  1564. compresspath(base, path, len)
  1565. char *base, *path;
  1566. int  len;
  1567. {
  1568.     register int i;
  1569.     int  depth = 0;
  1570.     char *p;
  1571.     char *stack[32];
  1572.     char  pathbuf[NLINE];
  1573.  
  1574. #define PUSHD(X)  (stack[depth++] = X)
  1575. #define POPD()    ((depth > 0) ? stack[--depth] : "")
  1576.  
  1577.     if(*path == '~'){
  1578.     fixpath(path, len);
  1579.     strcpy(pathbuf, path);
  1580.     }
  1581.     else if(*path != C_FILESEP)
  1582.       sprintf(pathbuf, "%s%c%s", base, C_FILESEP, path);
  1583.     else
  1584.       strcpy(pathbuf, path);
  1585.  
  1586.     p = &pathbuf[0];
  1587.     for(i=0; pathbuf[i] != '\0'; i++){        /* pass thru path name */
  1588.     if(pathbuf[i] == '/'){
  1589.         if(p != pathbuf)
  1590.           PUSHD(p);                /* push dir entry */
  1591.  
  1592.         p = &pathbuf[i+1];            /* advance p */
  1593.         pathbuf[i] = '\0';            /* cap old p off */
  1594.         continue;
  1595.     }
  1596.  
  1597.     if(pathbuf[i] == '.'){            /* special cases! */
  1598.         if(pathbuf[i+1] == '.'         /* parent */
  1599.            && (pathbuf[i+2] == '/' || pathbuf[i+2] == '\0')){
  1600.         if(!strcmp(POPD(), ""))        /* bad news! */
  1601.           return(0);
  1602.  
  1603.         i += 2;
  1604.         p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
  1605.         }
  1606.         else if(pathbuf[i+1] == '/' || pathbuf[i+1] == '\0'){
  1607.         i++;
  1608.         p = (pathbuf[i] == '\0') ? "" : &pathbuf[i+1];
  1609.         }
  1610.     }
  1611.     }
  1612.  
  1613.     if(*p != '\0')
  1614.       PUSHD(p);                    /* get last element */
  1615.  
  1616.     path[0] = '\0';
  1617.     for(i = 0; i < depth; i++){
  1618.     strcat(path, S_FILESEP);
  1619.     strcat(path, stack[i]);
  1620.     }
  1621.  
  1622.     return(1);                    /* everything's ok */
  1623. }
  1624.  
  1625.  
  1626. /*
  1627.  * tmpname - return a temporary file name in the given buffer
  1628.  */
  1629. void
  1630. tmpname(name)
  1631. char *name;
  1632. {
  1633.     sprintf(name, "/tmp/pico.%d", getpid());    /* tmp file name */
  1634. }
  1635.  
  1636.  
  1637. /*
  1638.  * Take a file name, and from it
  1639.  * fabricate a buffer name. This routine knows
  1640.  * about the syntax of file names on the target system.
  1641.  * I suppose that this information could be put in
  1642.  * a better place than a line of code.
  1643.  */
  1644. void
  1645. makename(bname, fname)
  1646. char    bname[];
  1647. char    fname[];
  1648. {
  1649.     register char   *cp1;
  1650.     register char   *cp2;
  1651.  
  1652.     cp1 = &fname[0];
  1653.     while (*cp1 != 0)
  1654.       ++cp1;
  1655.  
  1656.     while (cp1!=&fname[0] && cp1[-1]!='/')
  1657.       --cp1;
  1658.  
  1659.     cp2 = &bname[0];
  1660.     while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
  1661.       *cp2++ = *cp1++;
  1662.  
  1663.     *cp2 = 0;
  1664. }
  1665.  
  1666.  
  1667. /*
  1668.  * copy - copy contents of file 'a' into a file named 'b'.  Return error
  1669.  *        if either isn't accessible or is a directory
  1670.  */
  1671. copy(a, b)
  1672. char *a, *b;
  1673. {
  1674.     int    in, out, n, rv = 0;
  1675.     char   *cb;
  1676.     struct stat tsb, fsb;
  1677.     extern int  errno;
  1678.  
  1679.     if(stat(a, &fsb) < 0){        /* get source file info */
  1680.     emlwrite("Can't Copy: %s", errstr(errno));
  1681.     return(-1);
  1682.     }
  1683.  
  1684.     if(!(fsb.st_mode&S_IREAD)){        /* can we read it? */
  1685.     emlwrite("\007Read permission denied: %s", a);
  1686.     return(-1);
  1687.     }
  1688.  
  1689.     if((fsb.st_mode&S_IFMT) == S_IFDIR){ /* is it a directory? */
  1690.     emlwrite("\007Can't copy: %s is a directory", a);
  1691.     return(-1);
  1692.     }
  1693.  
  1694.     if(stat(b, &tsb) < 0){        /* get dest file's mode */
  1695.     switch(errno){
  1696.       case ENOENT:
  1697.         break;            /* these are OK */
  1698.       default:
  1699.         emlwrite("\007Can't Copy: %s", errstr(errno));
  1700.         return(-1);
  1701.     }
  1702.     }
  1703.     else{
  1704.     if(!(tsb.st_mode&S_IWRITE)){    /* can we write it? */
  1705.         emlwrite("\007Write permission denied: %s", b);
  1706.         return(-1);
  1707.     }
  1708.  
  1709.     if((tsb.st_mode&S_IFMT) == S_IFDIR){    /* is it directory? */
  1710.         emlwrite("\007Can't copy: %s is a directory", b);
  1711.         return(-1);
  1712.     }
  1713.  
  1714.     if(fsb.st_dev == tsb.st_dev && fsb.st_ino == tsb.st_ino){
  1715.         emlwrite("\007Identical files.  File not copied", NULL);
  1716.         return(-1);
  1717.     }
  1718.     }
  1719.  
  1720.     if((in = open(a, O_RDONLY)) < 0){
  1721.     emlwrite("Copy Failed: %s", errstr(errno));
  1722.     return(-1);
  1723.     }
  1724.  
  1725.     if((out=creat(b, fsb.st_mode&0xfff)) < 0){
  1726.     emlwrite("Can't Copy: %s", errstr(errno));
  1727.     close(in);
  1728.     return(-1);
  1729.     }
  1730.  
  1731.     if((cb = (char *)malloc(NLINE*sizeof(char))) == NULL){
  1732.     emlwrite("Can't allocate space for copy buffer!", NULL);
  1733.     close(in);
  1734.     close(out);
  1735.     return(-1);
  1736.     }
  1737.  
  1738.     while(1){                /* do the copy */
  1739.     if((n = read(in, cb, NLINE)) < 0){
  1740.         emlwrite("Can't Read Copy: %s", errstr(errno));
  1741.         rv = -1;
  1742.         break;            /* get out now */
  1743.     }
  1744.  
  1745.     if(n == 0)            /* done! */
  1746.       break;
  1747.  
  1748.     if(write(out, cb, n) != n){
  1749.         emlwrite("Can't Write Copy: %s", errstr(errno));
  1750.         rv = -1;
  1751.         break;
  1752.     }
  1753.     }
  1754.  
  1755.     free(cb);
  1756.     close(in);
  1757.     close(out);
  1758.     return(rv);
  1759. }
  1760.  
  1761.  
  1762. /*
  1763.  * Open a file for writing. Return TRUE if all is well, and FALSE on error
  1764.  * (cannot create).
  1765.  */
  1766. ffwopen(fn)
  1767. char    *fn;
  1768. {
  1769.     extern FILE *ffp;
  1770.  
  1771.     if ((ffp=fopen(fn, "w")) == NULL) {
  1772.         emlwrite("Cannot open file for writing", NULL);
  1773.         return (FIOERR);
  1774.     }
  1775.  
  1776.     return (FIOSUC);
  1777. }
  1778.  
  1779.  
  1780. /*
  1781.  * Close a file. Should look at the status in all systems.
  1782.  */
  1783. ffclose()
  1784. {
  1785.     extern FILE *ffp;
  1786.  
  1787.     if (fclose(ffp) != FALSE) {
  1788.         emlwrite("Error closing file", NULL);
  1789.         return(FIOERR);
  1790.     }
  1791.  
  1792.     return(FIOSUC);
  1793. }
  1794.  
  1795.  
  1796. /*
  1797.  * P_open - run the given command in a sub-shell returning a file pointer
  1798.  *        from which to read the output
  1799.  *
  1800.  * note:
  1801.  *    For OS's other than unix, you will have to rewrite this function.
  1802.  *    Hopefully it'll be easy to exec the command into a temporary file, 
  1803.  *    and return a file pointer to that opened file or something.
  1804.  */
  1805. FILE *P_open(s)
  1806. char *s;
  1807. {
  1808.     return(popen(s, "r"));
  1809. }
  1810.  
  1811.  
  1812.  
  1813. /*
  1814.  * P_close - close the given descriptor
  1815.  *
  1816.  */
  1817. P_close(fp)
  1818. FILE *fp;
  1819. {
  1820.     return(pclose(fp));
  1821. }
  1822.  
  1823.  
  1824.  
  1825. /*
  1826.  * worthit - generic sort of test to roughly gage usefulness of using 
  1827.  *           optimized scrolling.
  1828.  *
  1829.  * note:
  1830.  *    returns the line on the screen, l, that the dot is currently on
  1831.  */
  1832. worthit(l)
  1833. int *l;
  1834. {
  1835.     int i;            /* l is current line */
  1836.     unsigned below;        /* below is avg # of ch/line under . */
  1837.  
  1838.     *l = doton(&i, &below);
  1839.     below = (i > 0) ? below/(unsigned)i : 0;
  1840.  
  1841.     return(below > 3);
  1842. }
  1843.  
  1844.  
  1845.  
  1846. /*
  1847.  * pico_new_mail - just checks mtime and atime of mail file and notifies user 
  1848.  *               if it's possible that they have new mail.
  1849.  */
  1850. pico_new_mail()
  1851. {
  1852.     int ret = 0;
  1853.     static time_t lastchk = 0;
  1854.     struct stat sbuf;
  1855.     char   inbox[256], *p;
  1856.  
  1857.     if(p = (char *)getenv("MAIL"))
  1858.       sprintf(inbox, p);
  1859.     else
  1860.       sprintf(inbox,"%s/%s", MAILDIR, getlogin());
  1861.  
  1862.     if(stat(inbox, &sbuf) == 0){
  1863.     ret = sbuf.st_atime <= sbuf.st_mtime &&
  1864.       (lastchk < sbuf.st_mtime && lastchk < sbuf.st_atime);
  1865.     lastchk = sbuf.st_mtime;
  1866.     return(ret);
  1867.     }
  1868.     else
  1869.       return(ret);
  1870. }
  1871.  
  1872.  
  1873.  
  1874. /*
  1875.  * time_to_check - checks the current time against the last time called 
  1876.  *                 and returns true if the elapsed time is > timeout
  1877.  */
  1878. time_to_check()
  1879. {
  1880.     static time_t lasttime = 0L;
  1881.  
  1882.     if(!timeout)
  1883.       return(FALSE);
  1884.  
  1885.     if(time((time_t *) 0) - lasttime > (time_t)timeout){
  1886.     lasttime = time((time_t *) 0);
  1887.     return(TRUE);
  1888.     }
  1889.     else
  1890.       return(FALSE);
  1891. }
  1892.  
  1893.  
  1894. /*
  1895.  * sstrcasecmp - compare two pointers to strings case independently
  1896.  */
  1897. sstrcasecmp(s1, s2)
  1898. QcompType *s1, *s2;
  1899. {
  1900.     register char *a, *b;
  1901.  
  1902.     a = *(char **)s1;
  1903.     b = *(char **)s2;
  1904.     while(toupper(*a) == toupper(*b++))
  1905.     if(*a++ == '\0')
  1906.       return(0);
  1907.  
  1908.     return(toupper(*a) - toupper(*--b));
  1909. }
  1910.  
  1911.  
  1912. /*
  1913.  * chkptinit -- initialize anything we need to support composer
  1914.  *        checkpointing
  1915.  */
  1916. chkptinit(file, n)
  1917.     char *file;
  1918.     int   n;
  1919. {
  1920.     unsigned pid;
  1921.     char    *chp;
  1922.  
  1923.     if(!file[0]){
  1924.     long gmode_save = gmode;
  1925.  
  1926.     if(gmode&MDCURDIR)
  1927.       gmode &= ~MDCURDIR;  /* so fixpath will use home dir */
  1928.  
  1929.     strcpy(file, "#picoXXXXX#");
  1930.     fixpath(file, NLINE);
  1931.     gmode = gmode_save;
  1932.     }
  1933.     else{
  1934.     int l = strlen(file);
  1935.  
  1936.     if(file[l-1] != '/'){
  1937.         file[l++] = '/';
  1938.         file[l]   = '\0';
  1939.     }
  1940.  
  1941.     strcpy(file + l, "#picoXXXXX#");
  1942.     }
  1943.  
  1944.     pid = (unsigned)getpid();
  1945.     for(chp = file+strlen(file) - 2; *chp == 'X'; chp--){
  1946.     *chp = (pid % 10) + '0';
  1947.     pid /= 10;
  1948.     }
  1949.  
  1950.     unlink(file);
  1951. }
  1952.  
  1953.  
  1954. #ifdef    TIOCGWINSZ
  1955. /*
  1956.  * winch_handler - handle window change signal
  1957.  */
  1958. SIGTYPE winch_handler()
  1959. {
  1960.     signal(SIGWINCH, winch_handler);
  1961.     ttresize();
  1962. }
  1963. #endif    /* TIOCGWINSZ */
  1964.  
  1965.  
  1966. #ifdef    SIGCHLD
  1967. /*
  1968.  * child_handler - handle child status change signal
  1969.  */
  1970. SIGTYPE child_handler()
  1971. {
  1972.     pico_child_done = 1;
  1973.     if(pico_child_jmp_ok)
  1974.       longjmp(pico_child_state, 1);
  1975. }
  1976. #endif    /* SIGCHLD */
  1977.  
  1978.  
  1979. #if    defined(sv3) || defined(ct)
  1980. /* Placed by rll to handle the rename function not found in AT&T */
  1981. rename(oldname, newname)
  1982.     char *oldname;
  1983.     char *newname;
  1984. {
  1985.     int rtn;
  1986.  
  1987.     if ((rtn = link(oldname, newname)) != 0) {
  1988.     perror("Was not able to rename file.");
  1989.     return(rtn);
  1990.     }
  1991.  
  1992.     if ((rtn = unlink(oldname)) != 0)
  1993.       perror("Was not able to unlink file.");
  1994.  
  1995.     return(rtn);
  1996. }
  1997. #endif
  1998.  
  1999.  
  2000. #ifdef    MOUSE
  2001.  
  2002. /* 
  2003.  * init_mouse - check for xterm and initialize mouse tracking if present...
  2004.  */
  2005. init_mouse()
  2006. {
  2007.     if(mexist)
  2008.       return(TRUE);
  2009.  
  2010.     if(getenv("DISPLAY")){
  2011.     mouseon();
  2012.     return(mexist = TRUE);
  2013.     }
  2014.     else
  2015.       return(FALSE);
  2016. }
  2017.  
  2018.  
  2019. /* 
  2020.  * end_mouse - clear xterm mouse tracking if present...
  2021.  */
  2022. void
  2023. end_mouse()
  2024. {
  2025.     if(mexist){
  2026.     mexist = 0;            /* just see if it exists here. */
  2027.     mouseoff();
  2028.     }
  2029. }
  2030.  
  2031.  
  2032. /*
  2033.  * mouseexist - function to let outsiders know if mouse is turned on
  2034.  *              or not.
  2035.  */
  2036. mouseexist()
  2037. {
  2038.     return(mexist);
  2039. }
  2040.  
  2041.  
  2042. /*
  2043.  * mouseon - call made available for programs calling pico to turn ON the
  2044.  *           mouse cursor.
  2045.  */
  2046. void
  2047. mouseon()
  2048. {
  2049.     fputs(XTERM_MOUSE_ON, stdout);
  2050. }
  2051.  
  2052.  
  2053. /*
  2054.  * mouseon - call made available for programs calling pico to turn OFF the
  2055.  *           mouse cursor.
  2056.  */
  2057. void
  2058. mouseoff()
  2059. {
  2060.     fputs(XTERM_MOUSE_OFF, stdout);
  2061. }
  2062.  
  2063.  
  2064. /* 
  2065.  * checkmouse - look for mouse events in key menu and return 
  2066.  *              appropriate value.
  2067.  */
  2068. int
  2069. checkmouse(ch, down, mcol, mrow)
  2070. unsigned *ch;
  2071. int      down, mcol, mrow;
  2072. {
  2073.     static int oindex;
  2074.     register int k;            /* current bit/button of mouse */
  2075.     int i = 0, rv = 0;
  2076.     MENUITEM *mp;
  2077.  
  2078.     if(!mexist || mcol < 0 || mrow < 0)
  2079.       return(FALSE);
  2080.  
  2081.     if(down)            /* button down */
  2082.       oindex = -1;
  2083.  
  2084.     for(mp = mfunc; mp; mp = mp->next)
  2085.       if(mp->action && M_ACTIVE(mrow, mcol, mp))
  2086.     break;
  2087.  
  2088.     if(mp){
  2089.     unsigned long r;
  2090.  
  2091.     r = (*mp->action)(down ? M_EVENT_DOWN : M_EVENT_UP,
  2092.               mrow, mcol, M_BUTTON_LEFT, 0);
  2093.     if(r & 0xffff){
  2094.         *ch = (unsigned)((r>>16)&0xffff);
  2095.         rv  = TRUE;
  2096.     }
  2097.     }
  2098.     else{
  2099.     while(1){            /* see if we understand event */
  2100.         if(i >= 12){
  2101.         i = -1;
  2102.         break;
  2103.         }
  2104.  
  2105.         if(M_ACTIVE(mrow, mcol, &menuitems[i]))
  2106.           break;
  2107.  
  2108.         i++;
  2109.     }
  2110.  
  2111.     if(down){            /* button down */
  2112.         oindex = i;            /* remember where */
  2113.         if(i != -1
  2114.            && menuitems[i].label_hiliter != NULL
  2115.            && menuitems[i].val != mnoop)  /* invert label */
  2116.           (*menuitems[i].label_hiliter)(1, &menuitems[i]);
  2117.     }
  2118.     else{                /* button up */
  2119.         if(oindex != -1){
  2120.         if(i == oindex){
  2121.             *ch = menuitems[i].val;
  2122.             rv = TRUE;
  2123.         }
  2124.         }
  2125.     }
  2126.     }
  2127.  
  2128.     /* restore label */
  2129.     if(!down
  2130.        && oindex != -1
  2131.        && menuitems[oindex].label_hiliter != NULL
  2132.        && menuitems[oindex].val != mnoop)
  2133.       (*menuitems[oindex].label_hiliter)(0, &menuitems[oindex]);
  2134.  
  2135.     return(rv);
  2136. }
  2137.  
  2138.  
  2139. /*
  2140.  * invert_label - highlight the label of the given menu item.
  2141.  */
  2142. void
  2143. invert_label(state, m)
  2144. int state;
  2145. MENUITEM *m;
  2146. {
  2147.     unsigned i, j;
  2148.     int   r, c, p, oldr, oldc, col_offset;
  2149.     char *lp;
  2150.  
  2151.     /*
  2152.      * Leave the command name bold
  2153.      */
  2154.     col_offset = (state || !(lp=strchr(m->label, ' '))) ? 0 : (lp - m->label);
  2155.     movecursor((int)m->tl.r, (int)m->tl.c + col_offset);
  2156.     (*term.t_rev)(state);
  2157.  
  2158.     for(i = m->tl.r; i <= m->br.r; i++)
  2159.       for(j = m->tl.c + col_offset; j <= m->br.c; j++)
  2160.     if(i == m->lbl.r && j == m->lbl.c + col_offset && m->label){
  2161.         lp = m->label + col_offset;        /* show label?? */
  2162.         while(*lp && j++ < m->br.c)
  2163.           putc(*lp++, stdout);
  2164.  
  2165.         continue;
  2166.     }
  2167.     else
  2168.       putc(' ', stdout);
  2169.  
  2170.     if(state)
  2171.       (*term.t_rev)(0);  /* turn inverse back off */
  2172. }
  2173. #endif    /* MOUSE */
  2174.